package coalition;
import coalition.types.CoalitionType;
import coalition.types.OneAttributeCoalition;
import coalition.types.OneFixedAndOneDisjunctionCoalition;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import combinations.Combinations;
import combinations.RawCombinations;

import population.Population;


public class CoalitionFormation {

	Population population;
	int[] dimensionSizes;
	int maximumDisjunctiveSize;
	
	Combinations defaultCombinationsScheme = new RawCombinations();
	
	private HashMap<Integer, Combinations> combinationsSchemes = new HashMap<Integer, Combinations>();
	
	public void setCombinationsScheme(Combinations combinationsScheme, int index) {
		combinationsSchemes.put(index, combinationsScheme);
	}
	
	private void setDefaultCombinations(){
		for(int i = 0; i < dimensionSizes.length; i++){
			combinationsSchemes.put(i, defaultCombinationsScheme );
		}
	}

	public Population getPopulation() {
		return population;
	}

	public int[] getDimensionSizes() {
		return dimensionSizes;
	}

	public int getMaximumDisjunctiveSize() {
		return maximumDisjunctiveSize;
	}

	public CoalitionFormation(Population population){
		this.population = population;
		this.dimensionSizes = population.getDimensionSizes();
		maximumDisjunctiveSize = population.getMaximumDisjunctiveSize();
		setDefaultCombinations();
	}
	
	//private List<Coalition> getAllTwoAttributeCoalitions(){
	//	List<Coalition> coalitions = new Vector<Coalition>();
	//	return coalitions;
	//}	
	
	private List<Coalition> getAllLegalCoalitions(){
		CoalitionType oneAttributeCoalition = new OneAttributeCoalition(this, combinationsSchemes);
		//List<Coalition> oneAttributeCoalitions = getAllOneAttributeCoalitions();
		List<Coalition> oneAttributeCoalitions = oneAttributeCoalition.getCoalitions();
		List<Coalition> allLegalCoalitions = new ArrayList<Coalition>();
		allLegalCoalitions.addAll(oneAttributeCoalitions);
		if(dimensionSizes.length >= 2){
			CoalitionType oneFixedAndOneDisjunctionCoalition = 
				new OneFixedAndOneDisjunctionCoalition(this, combinationsSchemes);
			//List<Coalition> twoAttributeCoalitions = getAllTwoAttributeCoalitions();
			List<Coalition> twoAttributeCoalitions = oneFixedAndOneDisjunctionCoalition.getCoalitions();
			allLegalCoalitions.addAll(twoAttributeCoalitions);
		}
		return allLegalCoalitions;
	}
	
	public List<CoalitionWithInfo> computeCoalitions(){
		if(!population.checkAllSectorsFilled())
			throw new IllegalStateException("can't compute colaitions since not all" +
					" sectors have been instantiated");
		List<Coalition> coalitions = getAllLegalCoalitions();
		ArrayList<CoalitionWithInfo> coalitionsWithInfo = new ArrayList<CoalitionWithInfo>();
		for(Coalition coalition : coalitions){
			int populationSize = coalition.getCoalitionPopulationSize();
			float modulatedSize = coalition.getModulatedCoalitionPopulationSize();
			CoalitionWithInfo coalitionWithInfo =
				new CoalitionWithInfo(coalition, populationSize, modulatedSize);
			coalitionsWithInfo.add(coalitionWithInfo);
		}
		return coalitionsWithInfo;
	}
	
	/* (non-Javadoc)
	 * @see PopulationInterface#getWinningCoalition()
	 */
	public CoalitionWithInfo getWinningCoalition(){
		float totalModulatedSizeOfPopulation = population.getTotalModulatedSize();
		float sizeThreshhold = 0.5f * totalModulatedSizeOfPopulation;
		List<CoalitionWithInfo> allCoalitions = computeCoalitions();
		Collections.sort(allCoalitions);
		Collections.reverse(allCoalitions);
		for(CoalitionWithInfo coalitionWithInfo : allCoalitions){
			if(coalitionWithInfo.getCoalitionModulatedSize() >= sizeThreshhold)
				return coalitionWithInfo;
		}
		return null;
		//return allCoalitions.get(0);
	}
	
	public CoalitionWithInfo getPreferredCoalition(int[] querySectorIndices){
		float totalModulatedSizeOfPopulation = population.getTotalModulatedSize();
		float sizeThreshhold = 0.5f * totalModulatedSizeOfPopulation;		
		List<CoalitionWithInfo> allCoalitions = computeCoalitions();
		//PopulationSector populationSector = multiArray.getElementAtIndex(querySectorIndices);
		Collections.sort(allCoalitions);
		Collections.reverse(allCoalitions);
		for(CoalitionWithInfo coalitionWithInfo : allCoalitions){
			for(int[] sectorIndices : coalitionWithInfo.getCoalition().sectorsInvolved){
				if(compareSectorIndices(sectorIndices, querySectorIndices))
					if(coalitionWithInfo.getCoalitionModulatedSize() >= sizeThreshhold)
						return coalitionWithInfo;
			}
			
		}
		return null;
	}	
		
	static boolean compareSectorIndices(int[] indices1, int[] indices2){
		if(indices1.length != indices2.length)
			throw new IllegalArgumentException("unequal sector index arrays being compared");
		for(int i = 0; i < indices1.length; i++)
			if(indices1[i] != indices2[i]) return false;
		return true;
	}		
	
}
